package cc.magicjson.design.principles.openclose.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

/**
 * {@link}
 *
 * @author <a href="mailto:it_magicjson@163.com">MagicJson</a>
 * @since 1.0.0
 */
public class NumberByReentrantLockPrinter extends BaseNumberPrinter {

	private final int n;
	private int counter = 1;
	private final ReentrantLock lock = new ReentrantLock();

	private final Condition zeroTurn = lock.newCondition();
	private final Condition oddCondition = lock.newCondition();
	private final Condition evenCondition = lock.newCondition();

	private boolean isTurnZero = true;

	public NumberByReentrantLockPrinter(int n) {
		this.n = n;
	}

	public void printZeroNumber(IntConsumer intConsumer) {
		for (int i = 0; i < n; i++) {
			lock.lock();
			try {
				while (!isTurnZero) {
					zeroTurn.await();
				}
				intConsumer.accept(0);
				isTurnZero = false;
				if (counter % 2 == 0) {
					evenCondition.signal();
				} else {
					oddCondition.signal();
				}

			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			} finally {
				lock.unlock();
			}
		}

	}

	public void printEvenNumber(IntConsumer intConsumer) {
		for (int i = 2; i <= n; i += 2) {
			lock.lock();
			try {
				while (isTurnZero || counter % 2 != 0) {
					evenCondition.await();
				}
				intConsumer.accept(counter);
				counter++;
				isTurnZero = true;
				zeroTurn.signal();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			} finally {
				lock.unlock();
			}

		}
	}

	public void printOddNumber(IntConsumer intConsumer) {
		for (int i = 1; i <= n; i += 2) {
			lock.lock();
			try {
				while (isTurnZero || counter % 2 == 0) {
					oddCondition.await();
				}
				intConsumer.accept(counter);
				counter++;
				isTurnZero = true;
				zeroTurn.signal();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			} finally {
				lock.unlock();
			}

		}
	}
}
